﻿using adb;
using Masuit.Tools;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using MQTTnet;
using MQTTnet.Client;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using utils;

namespace Notication.AppCode
{
    public class SendModel { 
        public tAlarmMsg model { get; set; }
        public vAlarmSendNotication UserModel { get; set; }

        public override int GetHashCode()
        {
            return $"{this.UserModel.alarmUser_userId_fk}_{this.UserModel.alarmPowerStation_powerStationId_fk}_{this.UserModel.deviceTopic}".GetHashCode();
        }
    }
    /// <summary>
    /// 检查传感器上传数据是否合适触发警报
    /// </summary>
    public class CheckData
    {
        IMqttClient client;

        IConfigurationRoot configuration;
        List<vAlarmSendNotication> alarmList = new List<vAlarmSendNotication>();
        private readonly RedisUtil _redisUtil;
        dbBase dbBase = new dbBase();
        public CheckData(IConfigurationRoot config,RedisUtil redis)
        {
            this.configuration = config;
            this._redisUtil = redis;

            var optionsBuilder = new MqttClientOptionsBuilder();
            optionsBuilder.WithTcpServer(this.configuration["mqttServiceIp"], this.configuration["mqttServicePort"].ConvertTo<int>());
            optionsBuilder.WithCredentials("admin", "admin");
            optionsBuilder.WithClientId(this.configuration["mqttClientId"]);
            var clienOption = optionsBuilder.Build();

            client = new MqttFactory().CreateMqttClient();
            client.ConnectedAsync += Client_ConnectedAsync;
            client.ApplicationMessageReceivedAsync += Client_ApplicationMessageReceivedAsync;

            // 重连机制
            client.DisconnectedAsync += async e =>
            {
                Console.WriteLine("与服务器之间的连接断开了，正在尝试重新连接");
                // 等待 5s 时间
                await Task.Delay(TimeSpan.FromSeconds(5));
                try
                {
                    // 重新连接
                    await this.client.ConnectAsync(clienOption);
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"重新连接服务器失败:{ex}");
                }
            };
            this.client.ConnectAsync(clienOption);

            Task fun2 = new Task(async () => {
                await this.UploadAlarmList();
            });
            fun2.Start();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        private async Task Client_ApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs arg)
        {
            try
            {
                var jsonStr = Encoding.UTF8.GetString(arg.ApplicationMessage.Payload);
                var m = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, decimal>>(jsonStr);
                if (m != null && m.ContainsKey("alarm") && m["alarm"] > 0)
                {
                    await SendNotication(arg.ApplicationMessage.Topic);
                }
            }
            catch (Exception ex)
            {
                writeExceptionLogUtil.writeLogException(ex,"/root/logs","Notication");
                Console.WriteLine($"警报数据出错:{arg.ApplicationMessage.Topic}");
                Console.WriteLine($"获取内容:{Encoding.UTF8.GetString(arg.ApplicationMessage.Payload)}");
                Console.WriteLine(ex.ToString());
            }
        }

        private async Task Client_ConnectedAsync(MqttClientConnectedEventArgs arg)
        {
            await this.RegsterTopic();
            //return Task.CompletedTask;
        }


        public async Task RegsterTopic() {
            using (var conn = this.dbBase.CreateMyDb())
            {
                var alerts = await conn.tPowerStationDevice.Where(x => x.deviceType_keyId_fk == 820075 && x.dataState == 0).ToListAsync();
                await this.client.SubscribeAsync(new MqttClientSubscribeOptions
                {
                    TopicFilters = alerts.Select(x=>new MQTTnet.Packets.MqttTopicFilter() { Topic = x.deviceTopic}).ToList(),
                });
            }
           
            //return Task.CompletedTask;
        }

        /// <summary>
        /// 更新警报信息,50秒钟一次
        /// </summary>
        /// <returns></returns>
        public async Task UploadAlarmList() {
            using (var db = this.dbBase.CreateMyDb())
            {
                int count = 0;
                decimal alarmId = 0;
                while (true)
                {
                    //var alarmId1 = ((await db.tAlarmUser.Select(x => x.alarmUserId).OrderByDescending(x => x).FirstOrDefaultAsync()));
                    //var count1 = await db.vAlarmSendNotication.CountAsync();
                    //if (count != count1 ||  alarmId1 != alarmId) {
                        this.alarmList = await db.vAlarmSendNotication.ToListAsync();
                    //}
                    //count = count1;
                    //alarmId = alarmId1;
                    Thread.Sleep(50000);
                }
            }
        }

        /// <summary>
        /// 发送警报通知
        /// </summary>
        /// <param name="m"></param>
        /// <returns></returns>
        public async Task SendNotication(string Topic) {
            if (string.IsNullOrWhiteSpace(Topic)) return;

            var lst = this.alarmList.Where(x => x.deviceTopic == Topic).ToList();
            if (!lst.Any()) return;
            using (var conn = dbBase.CreateMyDb())
            {
                var i = lst.FirstOrDefault();
                if (!await CanTrigger(Topic, i))
                    return;
                var alarm = new tAlarmMsg
                {
                    addTime = DateTime.Now,
                    alarmMsg_alarmId_fk = i.alarmId
                };
                await conn.tAlarmMsg.AddAsync(alarm);
                await conn.SaveChangesAsync();

                foreach (var item in lst)
                {
                    var model = item;
                    var msg = new tMessage()
                    {
                        isRead = false,
                        messageContent = $"[{model.powerStationName}][{GetAlarmLevel(model)}]系统产生{model.explain}警报,警报ID为:{alarm.alarmMsgId}",
                        messageType = "警报消息",
                        messageOfPowerStation_powerStationId_fk = model.alarmPowerStation_powerStationId_fk,
                        messageOfUser_userId_fk = model.alarmUser_userId_fk
                    };
                    await conn.tMessage.AddAsync(msg);
                    await conn.SaveChangesAsync();
                    if ((model.pushTypeValue & 1) == 1)
                    {
                        Console.WriteLine("开始推送网页:" + Topic);
                        await this.client.PublishStringAsync("/notication/web/" + msg.messageOfUser_userId_fk, System.Text.Json.JsonSerializer.Serialize(msg));
                    }
                    if ((model.pushTypeValue & 2) == 2)
                    {
                        Console.WriteLine("开始推送APP:" + Topic);
                        var phoneSN = await conn.tUserDeviceSN.Where(x => x.phoneDevice_userId_fk == model.alarmUser_userId_fk).Select(x => x.phoneSN).ToListAsync();
                        foreach (var sn in phoneSN)
                        {
                            if (!string.IsNullOrEmpty(sn))
                            {
                                using(HttpClient httpClient = new HttpClient())
                                {
                                    try
                                    {
                                        var respose = await httpClient.GetAsync($"http://192.168.0.110:8080/push/send?title={WebUtility.UrlEncode("告警消息")}&msg={WebUtility.UrlEncode(msg.messageContent)}&rid={sn}&extras={WebUtility.UrlEncode("{\"url\":\"http://192.168.0.110:8081/#/views/PushMsg\"}")}");
                                        var text = await respose.Content.ReadAsStringAsync();
                                        Console.WriteLine("发送手机通知:" + text);
                                    }
                                    catch (Exception ex)
                                    {
                                        writeExceptionLogUtil.writeLogException(ex,"/root/logs","Notication");
                                        Console.WriteLine("发送移动APP通知失败:" + ex.Message);
                                    }
                                }
                            }
                        }
                    }
                    if ((model.pushTypeValue & 4) == 4)
                    {
                        Console.WriteLine("发送短信通知:" + Topic);
                        await SendSms.SendMsg(model.phone, GetAlarmLevel(model), model.explain, model.powerStationName);
                    }
                }
           
            }
        }

        /// <summary>
        /// 根据topic对应的预警级别获取报警间隔来判断当前是否报警
        /// </summary>
        /// <param name="Topic"></param>
        /// <returns>是否报警</returns>
        async Task<bool> CanTrigger(string Topic,vAlarmSendNotication m)
        {
            var cacheKey = $"Trigger:{Topic}";
            var value = await _redisUtil.GetStringKey<string>(cacheKey);
            Console.WriteLine(value);
            if (value != null)
            {
                return false;
            }
            else
            {
                //高级警报报警间隔1分钟，中级警报间隔10分钟，低级警报间隔1小时
                int expire = 0;
                if(m.alamLevel == 1 )
                    expire = 1;
                else if (m.alamLevel == 2)
                    expire = 10;
                else if (m.alamLevel == 3)
                    expire = 60;
                return await _redisUtil.SetKeyExpire(cacheKey,"1", expire);
            }
        }
        public string GetAlarmLevel(vAlarmSendNotication m) {
            if (m.alamLevel == 1)
                return "高级警报";
            if (m.alamLevel == 2)
                return "中级警报";
            return "低级警报";
        }
    }
}
